package com.iflytek.jzcpx.procuracy.card.component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iflytek.jzcpx.procuracy.card.common.CardConstants;
import com.iflytek.jzcpx.procuracy.card.common.enums.CardFieldEnum;
import com.iflytek.jzcpx.procuracy.card.common.enums.XYRFieldNameEnum;
import com.iflytek.jzcpx.procuracy.card.common.enums.XyrCbsjFieldEnum;
import com.iflytek.jzcpx.procuracy.card.config.ZdbmConfig;
import com.iflytek.jzcpx.procuracy.card.entity.CardFormField;
import com.iflytek.jzcpx.procuracy.card.pojo.ElleResultData;
import com.iflytek.jzcpx.procuracy.card.service.CardFormFieldService;
import com.iflytek.jzcpx.procuracy.tools.elle.ElleTextTypeEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 受理阶段案卡填充组件
 *
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019-08-29 19:34
 */
@Component
public class FillAcceptFormHelper extends AbstractFillFormHelper {
    private static final Logger logger = LoggerFactory.getLogger(FillAcceptFormHelper.class);

    @Autowired
    private ZdbmConfig zdbmConfig;
    @Autowired
    private CardFormFieldService cardFormFieldService;

    /**
     * 回填案卡
     *
     * @param cardFileId       案卡文件id
     * @param ajlbbm           赛威讯案件类别编码
     * @param elleTextTypeEnum 文书类型
     * @param bdjg             表单结构
     * @param elleResultData   elle引擎抽取出的书
     *
     * @return
     */
    public JSONObject fill(Long cardFileId, String ajlbbm, ElleTextTypeEnum elleTextTypeEnum, String bdjg,
            ElleResultData elleResultData) {
        JSONObject bdjgJSON = JSONObject.parseObject(bdjg);
        // 按照案卡表单结果转换
        List<ElleResultData.ExtractInfoVec> extractInfoVecList = elleResultData.getExtractInfoVec();
        if (CollectionUtils.isEmpty(extractInfoVecList)) {
            logger.info("要素引擎未抽取出数据");
            return bdjgJSON;
        }

        // 嫌疑人
        processXyr(cardFileId, ajlbbm, elleTextTypeEnum, extractInfoVecList, bdjgJSON,elleResultData);

        // 其他表单字段
        processOthers(cardFileId, ajlbbm, elleTextTypeEnum, elleResultData, bdjgJSON);

        return bdjgJSON;
    }

    /** 处理其他表单字段 */
    private void processOthers(Long cardFileId, String ajlbbm, ElleTextTypeEnum elleTextTypeEnum, ElleResultData elleResultData, JSONObject bdjgJSON) {
        Map<String, JSONObject> stbmMap = groupByStbm(bdjgJSON);
        // 将表单及其字段按名称分组, 便于获取
        Map<String, Map<String, JSONObject>> stbmAndZdmcMap = groupZdsjByStbmAndZdmc(bdjgJSON);

        // 抽取出的字段最终需要落库
        List<CardFormField> newFields = new ArrayList<>();
        for (CardFieldEnum cardFieldEnum : CardFieldEnum.values()) {
            Collection<String> stbms = cardFieldEnum.getStbm();
            String zdmc = cardFieldEnum.getZdmc();
            Collection<ElleTextTypeEnum> supportElleTextTypeEnums = cardFieldEnum.getSupportElleTextType();

            // 该字段无法从该文书中被抽取
            if (!supportElleTextTypeEnums.contains(elleTextTypeEnum)) {
                continue;
            }

            for (String stbm : stbms) {
                Map<String, JSONObject> zdsjMap = stbmAndZdmcMap.get(stbm);
                if (MapUtils.isEmpty(zdsjMap) || zdsjMap.get(zdmc) == null) {
                    continue;
                }

                JSONObject zdsj = zdsjMap.get(zdmc);
                Function<ElleResultData, ElleResultData.Item> func = cardFieldEnum.getElleFieldExtractor();
                ElleResultData.Item item=null;
                if (null == func) {
    				// 使用映射函数取值
    				boolean hasMapFunc = cardFieldEnum.isHasMapFunc();
    				if (hasMapFunc) {
    					String cqxzzjg = cardFieldEnum.getMappingFunc().apply(elleResultData);
    					if (StringUtils.isNotEmpty(cqxzzjg)) {
    						item=new  ElleResultData.Item();
    						item.setMean(cqxzzjg);
    					}
    				}
    				Function<ElleResultData, ElleResultData.Item[]> xyrFun = cardFieldEnum.getXyrFieldExtractFunc();
    				if (null != xyrFun) {
    					ElleResultData.Item[] items = xyrFun.apply(elleResultData);
    					if (ArrayUtils.isNotEmpty(items)) {
    						item=items[0];
    					}
    				}
    			}
                if(null == item && null != func) {
                	item = func.apply(elleResultData);
                }
    			if (null == item) {
    				// 是 、否 默认值 如字段 侦查机关建议是否建议适用认罪认罚
    				String defaultValue = cardFieldEnum.getDefaultValue();
    				if (StringUtils.isNotEmpty(defaultValue)) {
    					item=new  ElleResultData.Item();
						item.setMean(defaultValue);
    				}
    				else {
    					boolean hasMapFunc = cardFieldEnum.isHasMapFunc();
    					if (hasMapFunc) {
    						String cqxzzjg = cardFieldEnum.getMappingFunc().apply(elleResultData);
    						if (StringUtils.isNotEmpty(cqxzzjg)) {
    							item=new  ElleResultData.Item();
    							item.setMean(cqxzzjg);
    						}
    					}
    				}
    			}
    			if (null == item) {
    				continue;
    			}
                // 设置数据标识
                JSONObject cbsjJson = stbmMap.get(stbm);
                setSjbs(cbsjJson);
                if(zdmc.equals("YSAY")) {
                	System.out.println();
                }
                // 对字段文本值进行格式化，例如：抽取到出生日期1990年7月10日，则格式化为1990-07-10,
                // 如果字段文本值不符合规范(如: 19x4年7月10日)则返回空, 此时不再回填
                String zdwbz = cardFieldEnum.convertZdwbz(item.getMean());
                if (StringUtils.isBlank(zdwbz)) {
                    continue;
                }
                zdsj.put("bghzdwbz", zdwbz);
                String zdbmz = cardFieldEnum.convertZdbmzForBdjg(zdwbz, zdbmConfig);
                String zdlx=zdsj.getString("zdlx");
                CardFormField cardFormField = buildXyrBaseField(cardFileId, stbm);
                //文本型类型 编码值填写上文本值
                if(CardConstants.TEXT_ZDLX.contains(zdlx) ) {
                	zdsj.put("bghzdbmz", zdbmz);
                }else {
                	if(!zdbmz.equals(zdwbz)) {
                		zdsj.put("bghzdbmz", zdbmz);
                		cardFormField.setZdbmz(zdbmz);
                	}
                }
                // 设置变更时间
                fillBgsj(zdsj);
                cardFormField.setZdmc(zdmc);
                cardFormField.setZdwbz(zdwbz);
                newFields.add(cardFormField);
            }
        }

        // 保存字段到数据库
        boolean saveSuccess = cardFormFieldService.saveBatch(newFields);
        logger.info("保存其他表单字段, 结果:{}", saveSuccess ? "成功" : "失败");
    }
    
    

    /** 处理嫌疑人表单 */
    private void processXyr(Long cardFileId, String ajlbbm, ElleTextTypeEnum elleTextTypeEnum,
            List<ElleResultData.ExtractInfoVec> extractInfoVecList, JSONObject bdjg,ElleResultData elleResultData) {
        // ------------- 抽取字段并保存 --------------
        Collection<CardFormField> xyrAndCbsjFields = extractXyrAndCbsjFields(cardFileId, ajlbbm, elleTextTypeEnum,extractInfoVecList,elleResultData);
        if (CollectionUtils.isEmpty(xyrAndCbsjFields)) {
            logger.info("未能抽取到嫌疑人字段, 不再回填嫌疑人信息.");
            return;
        }
        else {
            // 批量保存字段数据
            boolean saveSuccess = cardFormFieldService.saveBatch(xyrAndCbsjFields);
            logger.info("批量保存嫌疑人及从表表单字段{}条, 结果:{}", CollectionUtils.size(xyrAndCbsjFields), saveSuccess ? "成功" : "失败");
        }

        // ------------- 回填表单 -------------
        // 从原始表单中获取嫌疑人表单
        String suspectTableName = parseXyrTableName(ajlbbm);
        if (StringUtils.isBlank(suspectTableName)) {
            logger.warn("无法根据案件类别编码：{} 获取对应的嫌疑人表名, 不再回填嫌疑人信息", ajlbbm);
            return;
        }

        JSONObject xyr = parseCbsjOfSingleItem(bdjg, suspectTableName);
        if (xyr == null) {
            logger.warn("原始表单没有传递嫌疑人表单：{}, 不再回填嫌疑人信息。", suspectTableName);
            return;
        }

        // 回填嫌疑人表单
        fillXyr(elleTextTypeEnum, xyrAndCbsjFields, bdjg, xyr);

    }

    /**
     * 回填嫌疑人及其从表字段
     *
     * @param elleTextTypeEnum 抽取的文书类型
     * @param xyrAndCbsjFields 抽取出嫌疑人及其从表数据
     * @param bdjg             原始表单结构
     * @param xyr              原始表单中的嫌疑人表单
     */
    private void fillXyr(ElleTextTypeEnum elleTextTypeEnum, Collection<CardFormField> xyrAndCbsjFields, JSONObject bdjg,
			JSONObject xyr) {
		// 嫌疑人数据按 primaryKey 进行分组, 同一组的所有字段代表来自同一个嫌疑人数据
		JSONObject xyrCopy = SerializationUtils.clone(xyr);
		JSONObject xyrSingleCopy=null;
		LinkedHashMap<String, List<CardFormField>> primaryKeyFieldsMap = xyrAndCbsjFields.stream()
				.sorted(Comparator.comparingLong(v -> Long.parseLong(v.getPrimaryKey())))
				.collect(Collectors.groupingBy(CardFormField::getPrimaryKey, LinkedHashMap::new, Collectors.toList()));
		int supcectSize = primaryKeyFieldsMap.values().size();
		for (int i = 0; i < supcectSize; i++) {
			List<CardFormField> cardFormFields = new ArrayList<>(primaryKeyFieldsMap.values()).get(i);
			// 过滤出嫌疑人字段和嫌疑人从表字段
			Map<Boolean, List<CardFormField>> fieldsMap = cardFormFields.stream()
					.collect(Collectors.groupingBy(f -> CardConstants.XYR_TABLENAMES.containsValue(f.getStbm())));
			List<CardFormField> xyrFields = fieldsMap.get(Boolean.TRUE);
			List<CardFormField> xyrCbsjFields = fieldsMap.get(Boolean.FALSE);

			if (CollectionUtils.isEmpty(xyrFields)) {
				continue;
			}

			// 多个嫌疑人处理逻辑: 最后一个嫌疑人时直接把数据更新到原始表单中的嫌疑人对象中,
			// 否则需要先克隆一份原始表单中的嫌疑人对象, 再将数据更新到克隆出的这个嫌疑人对象, 最后把克隆出的对象放入原始表单
			JSONArray cbsjArray = bdjg.getJSONArray("cbsj");

			// 将嫌疑人以及从表表单字段提前按名称分组, 提高查找效率
			Map<String, JSONObject> xyrZdmcMap = null;
			Map<String, Map<String, JSONObject>> xyrCbsjZdmcMap = null;

			if (i == 0) {
				xyrZdmcMap = mapZdsjByZdmc(xyr);
				xyrCbsjZdmcMap = mapZdsjOfCbsjByName(xyr);
			} else {
				 xyrSingleCopy = SerializationUtils.clone(xyrCopy);
				cbsjArray.add(xyrSingleCopy);
				xyrZdmcMap = mapZdsjByZdmc(xyrSingleCopy);
				xyrCbsjZdmcMap = mapZdsjOfCbsjByName(xyrSingleCopy);
				// 填充表单
				setSjbs(xyrSingleCopy);
			}

			// 填充表单
			setSjbs(xyr);
			// 回填嫌疑人表单
			for (CardFormField xyrField : xyrFields) {
				fillXyrZdsj(xyrZdmcMap, xyrField, elleTextTypeEnum);
			}

			// 回填嫌疑人从表表单
			if (CollectionUtils.isNotEmpty(xyrCbsjFields)) {
				// 从表数据按照primary_cb_key分组
				LinkedHashMap<String, List<CardFormField>> primaryCbKeyFieldsMap = xyrCbsjFields.stream()
						.sorted(Comparator.comparingLong(v -> Long.parseLong(v.getPrimaryCbkey()))).collect(Collectors
								.groupingBy(CardFormField::getPrimaryCbkey, LinkedHashMap::new, Collectors.toList()));
				Set<String> stbmSet = xyrCbsjFields.stream().map(CardFormField::getStbm).collect(Collectors.toSet());
				int k = 0;
				// 设置 sjbs
				for (String stbm : xyrCbsjZdmcMap.keySet()) {
					JSONObject cbsjJson = getCbsj(xyr, stbm);
					if (i > 0) {
						cbsjJson = getCbsj(xyrSingleCopy, stbm);
					}
					if (cbsjJson != null && stbmSet.contains(stbm)) {
						setSjbs(cbsjJson);
						for (Map.Entry<String, List<CardFormField>> entry : primaryCbKeyFieldsMap.entrySet()) {
							List<CardFormField> primaryCbKeyFieldsList = entry.getValue();
							if (k > 0) {
								// 拷贝一份强制措施到从表数据中
								JSONObject cbsjJsonCopy = SerializationUtils.clone(cbsjJson);
								JSONArray xyrcbsjArray = xyr.getJSONArray("cbsj");
								if (i > 0) {
									xyrcbsjArray = xyrSingleCopy.getJSONArray("cbsj");
								}
								xyrcbsjArray.add(cbsjJsonCopy);

								// 从新进行 数据按从表名称-字段名称进行分组
								Map<String, Map<String, JSONObject>> stbmMap = new HashMap<>();
								stbmMap.put(cbsjJsonCopy.getString("stbm"), mapZdsjByZdmc(cbsjJsonCopy));
								for (CardFormField xyrCbsjField : primaryCbKeyFieldsList) {
									fillXyrCbsjZdsj(stbmMap, xyrCbsjField, elleTextTypeEnum);
								}
							} else {
								for (CardFormField xyrCbsjField : primaryCbKeyFieldsList) {
									fillXyrCbsjZdsj(xyrCbsjZdmcMap, xyrCbsjField, elleTextTypeEnum);
								}
							}
							k++;
						}
					}
				}
			}
		}
	}

    /**
     * 回填嫌疑人从表字段数据
     *
     * @param xyrCbsjZdmcMap   嫌疑人从表字段名称 map
     * @param xyrCbsjField     已抽取的嫌疑人从表字段数据
     * @param elleTextTypeEnum 抽取的文书类型
     */
    private void fillXyrCbsjZdsj(Map<String, Map<String, JSONObject>> xyrCbsjZdmcMap, CardFormField xyrCbsjField,
            ElleTextTypeEnum elleTextTypeEnum) {
        String stbm = xyrCbsjField.getStbm();
        String zdmc = xyrCbsjField.getZdmc();
        String zdwbz = xyrCbsjField.getZdwbz();
        XyrCbsjFieldEnum xyrCbsjFieldEnum = XyrCbsjFieldEnum.parseByStbmAndZdmc(stbm, zdmc);
        String zdbmz = XyrCbsjFieldEnum.convertZdbmzForBdjg(xyrCbsjField);

        JSONObject zdsjJson = getMapValue(xyrCbsjZdmcMap, stbm, zdmc);
        if (zdsjJson == null) {
            return;
        }

        if (!xyrCbsjFieldEnum.getSupportElleTextType().contains(elleTextTypeEnum)) {
            // 如果该字段不在此文书类型则跳过
            return;
        }

        zdsjJson.put("bghzdwbz", zdwbz);
        
        String zdlx=zdsjJson.getString("zdlx");
        //文本型类型 编码值填写上文本值
        if(CardConstants.TEXT_ZDLX.contains(zdlx) ) {
        	zdsjJson.put("bghzdbmz", zdbmz);
        }else {
        	if(!zdbmz.equals(zdwbz)) {
        		zdsjJson.put("bghzdbmz", zdbmz);
        	}
        }
        // 设置变更时间
        fillBgsj(zdsjJson);
    }

    /**
     * 回填嫌疑人字段数据
     *
     * @param xyrZdmcMap       嫌疑人字段名称 map
     * @param xyrField         已抽取的嫌疑人字段数据
     * @param elleTextTypeEnum 抽取的文书类型
     */
    private void fillXyrZdsj(Map<String, JSONObject> xyrZdmcMap, CardFormField xyrField,
            ElleTextTypeEnum elleTextTypeEnum) {
        String zdmc = xyrField.getZdmc();
        String zdwbz = xyrField.getZdwbz();
        String zdbmz = XYRFieldNameEnum.convertZdbmzForBdjg(xyrField);

        JSONObject zdsjJson = xyrZdmcMap.get(zdmc);
        if (zdsjJson == null) {
            return;
        }

        zdsjJson.put("bghzdwbz", StringUtils.defaultString(zdwbz, ""));
        
        String zdlx=zdsjJson.getString("zdlx");
        //文本型类型 编码值填写上文本值
        if(CardConstants.TEXT_ZDLX.contains(zdlx) ) {
        	zdsjJson.put("bghzdbmz", zdbmz);
        }else {
        	if(!zdbmz.equals(zdwbz)) {
        		zdsjJson.put("bghzdbmz", zdbmz);
        	}
        }
        // 设置变更时间
        fillBgsj(zdsjJson);
    }

}
